package common.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Date;

/**
 * 日期时间工具类
 * <p>
 * 封装了基于 Java 8 Date-Time API 的常用日期与时间处理方法。
 * 功能包括：
 * <ul>
 *   <li>日期时间获取</li>
 *   <li>日期时间格式化与解析</li>
 *   <li>LocalDateTime、Date、时间戳之间的互转</li>
 *   <li>日期/时间的计算与比较</li>
 * </ul>
 * 工具类为 final 且构造器私有，禁止实例化。
 */
public final class DateUtils {

    private static final Logger logger = LoggerFactory.getLogger(DateUtils.class);

    /** 默认日期时间格式：yyyy-MM-dd HH:mm:ss */
    public static final String PATTERN_DEFAULT = "yyyy-MM-dd HH:mm:ss";

    /** 日期格式：yyyy-MM-dd */
    public static final String PATTERN_DATE_ONLY = "yyyy-MM-dd";

    /** 时间格式：HH:mm:ss */
    public static final String PATTERN_TIME_ONLY = "HH:mm:ss";

    /** 紧凑格式：yyyyMMddHHmmss */
    public static final String PATTERN_COMPACT = "yyyyMMddHHmmss";

    /** 中文日期时间格式：yyyy年MM月dd日 HH时mm分ss秒 */
    public static final String PATTERN_CHINESE = "yyyy年MM月dd日 HH时mm分ss秒";

    // 预定义的线程安全格式化器
    private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ofPattern(PATTERN_DEFAULT);
    private static final DateTimeFormatter DATE_ONLY_FORMATTER = DateTimeFormatter.ofPattern(PATTERN_DATE_ONLY);
    private static final DateTimeFormatter TIME_ONLY_FORMATTER = DateTimeFormatter.ofPattern(PATTERN_TIME_ONLY);

    private DateUtils() {
        throw new AssertionError("不允许实例化工具类");
    }

    /**
     * 获取当前日期时间。
     *
     * @return 当前的 {@link LocalDateTime}
     */
    public static LocalDateTime now() {
        return LocalDateTime.now();
    }

    /**
     * 获取当前日期。
     *
     * @return 当前的 {@link LocalDate}
     */
    public static LocalDate today() {
        return LocalDate.now();
    }

    /**
     * 获取当前时间戳（毫秒）。
     *
     * @return 当前时间戳（毫秒）
     */
    public static long currentTimestamp() {
        return System.currentTimeMillis();
    }

    /**
     * 按默认格式（yyyy-MM-dd HH:mm:ss）格式化日期时间。
     *
     * @param dateTime 待格式化的 {@link LocalDateTime}
     * @return 格式化后的字符串，若参数为 null 返回 null
     */
    public static String format(LocalDateTime dateTime) {
        if (dateTime == null) {
            return null;
        }
        return dateTime.format(DEFAULT_FORMATTER);
    }

    /**
     * 按指定格式格式化日期时间。
     *
     * @param dateTime 待格式化的 {@link LocalDateTime}
     * @param pattern  日期时间格式
     * @return 格式化后的字符串；若参数无效返回 null
     */
    public static String format(LocalDateTime dateTime, String pattern) {
        if (dateTime == null || pattern == null) {
            return null;
        }
        try {
            return dateTime.format(DateTimeFormatter.ofPattern(pattern));
        } catch (IllegalArgumentException e) {
            logger.warn("日期格式模式无效: {}", pattern, e);
            return null;
        }
    }

    /**
     * 格式化 LocalDate 为字符串（yyyy-MM-dd）。
     *
     * @param date 待格式化的 {@link LocalDate}
     * @return 格式化后的字符串；若参数为 null 返回 null
     */
    public static String formatDate(LocalDate date) {
        if (date == null) {
            return null;
        }
        return date.format(DATE_ONLY_FORMATTER);
    }

    /**
     * 按默认格式（yyyy-MM-dd HH:mm:ss）解析字符串为 LocalDateTime。
     *
     * @param dateTimeStr 日期时间字符串
     * @return 解析后的 {@link LocalDateTime}；解析失败或参数为空返回 null
     */
    public static LocalDateTime parse(String dateTimeStr) {
        if (dateTimeStr == null || dateTimeStr.trim().isEmpty()) {
            return null;
        }
        try {
            return LocalDateTime.parse(dateTimeStr, DEFAULT_FORMATTER);
        } catch (DateTimeParseException e) {
            logger.warn("解析日期时间字符串失败: {}", dateTimeStr, e);
            return null;
        }
    }

    /**
     * 按指定格式解析字符串为 LocalDateTime。
     *
     * @param dateTimeStr 日期时间字符串
     * @param pattern     日期时间格式
     * @return 解析后的 {@link LocalDateTime}；解析失败或参数无效返回 null
     */
    public static LocalDateTime parse(String dateTimeStr, String pattern) {
        if (dateTimeStr == null || dateTimeStr.trim().isEmpty() || pattern == null) {
            return null;
        }
        try {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
            return LocalDateTime.parse(dateTimeStr, formatter);
        } catch (DateTimeParseException e) {
            logger.warn("解析日期时间字符串失败: {}, 模式: {}", dateTimeStr, pattern, e);
            return null;
        } catch (IllegalArgumentException e) {
            logger.warn("日期格式模式无效: {}", pattern, e);
            return null;
        }
    }

    /**
     * 解析字符串为 LocalDate（yyyy-MM-dd）。
     *
     * @param dateStr 日期字符串
     * @return 解析后的 {@link LocalDate}；解析失败返回 null
     */
    public static LocalDate parseDate(String dateStr) {
        if (dateStr == null || dateStr.trim().isEmpty()) {
            return null;
        }
        try {
            return LocalDate.parse(dateStr, DATE_ONLY_FORMATTER);
        } catch (DateTimeParseException e) {
            logger.warn("解析日期字符串失败: {}", dateStr, e);
            return null;
        }
    }

    /**
     * LocalDateTime 转换为 Date。
     *
     * @param localDateTime 待转换的 {@link LocalDateTime}
     * @return 转换后的 {@link Date}；若参数为 null 返回 null
     */
    public static Date toDate(LocalDateTime localDateTime) {
        if (localDateTime == null) {
            return null;
        }
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * Date 转换为 LocalDateTime。
     *
     * @param date 待转换的 {@link Date}
     * @return 转换后的 {@link LocalDateTime}；若参数为 null 返回 null
     */
    public static LocalDateTime toLocalDateTime(Date date) {
        if (date == null) {
            return null;
        }
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    /**
     * 时间戳（毫秒）转换为 LocalDateTime。
     *
     * @param timestamp 时间戳（毫秒）
     * @return 转换后的 {@link LocalDateTime}
     */
    public static LocalDateTime fromTimestamp(long timestamp) {
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());
    }

    /**
     * LocalDateTime 转换为时间戳（毫秒）。
     *
     * @param localDateTime 待转换的 {@link LocalDateTime}
     * @return 转换后的时间戳；若参数为 null 返回 0
     */
    public static long toTimestamp(LocalDateTime localDateTime) {
        if (localDateTime == null) {
            return 0L;
        }
        return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * 计算两个日期之间的天数差。
     *
     * @param startDate 起始日期
     * @param endDate   结束日期
     * @return 天数差的绝对值；若任意参数为 null 返回 0
     */
    public static long daysBetween(LocalDate startDate, LocalDate endDate) {
        if (startDate == null || endDate == null) {
            return 0L;
        }
        return Math.abs(endDate.toEpochDay() - startDate.toEpochDay());
    }

    /**
     * 计算两个日期时间之间的分钟差。
     *
     * @param start 起始时间
     * @param end   结束时间
     * @return 分钟差的绝对值；若任意参数为 null 返回 0
     */
    public static long minutesBetween(LocalDateTime start, LocalDateTime end) {
        if (start == null || end == null) {
            return 0L;
        }
        Duration duration = Duration.between(start, end);
        return Math.abs(duration.toMinutes());
    }

    /**
     * 在指定日期时间上增加天数。
     *
     * @param dateTime 待操作的 {@link LocalDateTime}
     * @param days     增加的天数
     * @return 操作后的 {@link LocalDateTime}；若参数为 null 返回 null
     */
    public static LocalDateTime plusDays(LocalDateTime dateTime, long days) {
        if (dateTime == null) {
            return null;
        }
        return dateTime.plusDays(days);
    }

    /**
     * 在指定日期时间上减少天数。
     *
     * @param dateTime 待操作的 {@link LocalDateTime}
     * @param days     减少的天数
     * @return 操作后的 {@link LocalDateTime}；若参数为 null 返回 null
     */
    public static LocalDateTime minusDays(LocalDateTime dateTime, long days) {
        if (dateTime == null) {
            return null;
        }
        return dateTime.minusDays(days);
    }

    /**
     * 判断日期是否在指定范围内（包含边界）。
     *
     * @param date  待检查的日期
     * @param start 范围起始日期
     * @param end   范围结束日期
     * @return 若 date 在范围内返回 true，否则返回 false
     */
    public static boolean isWithinRange(LocalDate date, LocalDate start, LocalDate end) {
        if (date == null || start == null || end == null) {
            return false;
        }
        return !date.isBefore(start) && !date.isAfter(end);
    }

    /**
     * 判断给定日期是否为今天。
     *
     * @param date 待检查的日期
     * @return 若为今天返回 true，否则返回 false
     */
    public static boolean isToday(LocalDate date) {
        if (date == null) {
            return false;
        }
        return date.equals(LocalDate.now());
    }

    /**
     * 获取某天的开始时间（00:00:00）。
     *
     * @param date 指定日期
     * @return 当天的起始 {@link LocalDateTime}；若参数为 null 返回 null
     */
    public static LocalDateTime startOfDay(LocalDate date) {
        if (date == null) {
            return null;
        }
        return date.atStartOfDay();
    }

    /**
     * 获取某天的结束时间（23:59:59.999）。
     *
     * @param date 指定日期
     * @return 当天的结束 {@link LocalDateTime}；若参数为 null 返回 null
     */
    public static LocalDateTime endOfDay(LocalDate date) {
        if (date == null) {
            return null;
        }
        return date.atTime(23, 59, 59, 999000000);
    }

    /**
     * 根据生日计算年龄。
     *
     * @param birthDate 出生日期
     * @return 年龄（单位：岁）；若参数为 null 返回 0
     */
    public static int calculateAge(LocalDate birthDate) {
        if (birthDate == null) {
            return 0;
        }
        return Period.between(birthDate, LocalDate.now()).getYears();
    }
}
